package com.pyue.eam.admin.utils;

import com.pyue.eam.admin.api.except.ApiAsserts;
import com.pyue.eam.common.data.tenant.TenantContextHolder;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;

/**
 * 异步执行
 *
 * @author tanniao 2021-2-24 9:29:32
 */
@Slf4j
@Component
public class AsyncExecute implements DisposableBean {
    private static final ExecutorService executorService;

    static {
        executorService = new ThreadPoolExecutor(20, 20,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(100), new AsyncThreadFactory());
    }

    private AsyncRunnable getAsyncRunnable(Runnable run, boolean supportHttp) {
        AsyncRunnable runnable;
        if (supportHttp) {
            ApiAsserts.isTrue(TenantContextHolder.getTenantId() != null, "获取不到租户信息,停止进行异步操作");
            Long tenantId = TenantContextHolder.getTenantId();
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            SecurityContext context = SecurityContextHolder.getContext();
            runnable = new AsyncRunnable(run, (o) -> {
                RequestContextHolder.setRequestAttributes(requestAttributes);
                SecurityContextHolder.setContext(context);
                TenantContextHolder.setTenantId(tenantId);
            });
        } else {
            // 赋值子线程租户id 租户id可为空,方便junit使用
            Long tenantId = TenantContextHolder.getTenantId();
            runnable = new AsyncRunnable(run, (o) -> TenantContextHolder.setTenantId(tenantId));
        }
        return runnable;
    }

    public void execute(Runnable run) {
        execute(run, false);
    }

    public void executeAtHttp(Runnable run) {
        execute(run, true);
    }

    private void execute(Runnable run, boolean supportHttp) {
        AsyncRunnable asyncRunnable = getAsyncRunnable(run, supportHttp);
        executorService.execute(asyncRunnable);
    }

    public Future<?> submit(Runnable run) {
        return submit(run, false);
    }

    public Future<?> submitAtHttp(Runnable run) {
        return submit(run, true);
    }

    private Future<?> submit(Runnable run, boolean supportHttp) {
        AsyncRunnable asyncRunnable = getAsyncRunnable(run, supportHttp);
        return executorService.submit(asyncRunnable);
    }

    @Override
    public void destroy() throws Exception {
        if (executorService != null && !executorService.isShutdown()) {
            executorService.shutdown();
            try {
                if (!executorService.awaitTermination(120, TimeUnit.SECONDS)) {
                    executorService.shutdownNow();
                    if (!executorService.awaitTermination(120, TimeUnit.SECONDS)) {
                        log.warn("线程池终止失败");
                    }
                }
            } catch (InterruptedException ie) {
                executorService.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
    }

    @Getter
    static class AsyncRunnable implements Runnable {
        private final Runnable runnable;
        private final Consumer<Object> beforeExecute;

        public AsyncRunnable(Runnable runnable) {
            this(runnable, null);
        }

        public AsyncRunnable(Runnable runnable, Consumer<Object> beforeExecute) {
            this.runnable = runnable;
            this.beforeExecute = beforeExecute;
        }

        @Override
        public void run() {
            if (beforeExecute != null) {
                beforeExecute.accept(this);
            }
            this.runnable.run();
        }
    }

    static class AsyncThreadFactory implements ThreadFactory {
        private final AtomicLong threadCount = new AtomicLong();
        private final String name = "pyue-async-thread";

        @Override
        public Thread newThread(Runnable run) {
            Thread thread = new Thread(run);
            long count = threadCount.incrementAndGet();
            thread.setName(name + "-" + count);
            return thread;
        }
    }

}
